/** @file    SystemError.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SystemError.cpp 2833 2010-08-02 23:12:57Z alex $
 *  @brief
 * File containing methods for the wosh::SystemError class.
 * The header for this class can be found in SystemError.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/SystemError.h>
  #include <core/ObjectAllocator.h>
 #include <core/SystemMonitor.h>
 #include <core/Message.h>
 #include <core/Object.h>
 #include <core/LogContext.h>
 #include <core/DataModels.h>
 #include <stdarg.h>
 #include <stdio.h> // vsprintf


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::SystemError, "wosh::MessageContent", 1.01, _static_SystemError)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SystemError::SystemError()
	: MessageContent() {
	clear();
 }

SystemError::SystemError( long errorCode )
	: MessageContent() {
	clear();
	this->error_code = errorCode;
 }

SystemError::SystemError( long errorCode, const std::string& Description, ERROR_PRIORITY Priority )
	: MessageContent() {
	clear();
	this->error_code = errorCode;
	this->description = Description;
	this->priority = Priority;
 }

SystemError::SystemError(const SystemError& m)
	: MessageContent( (const MessageContent&)m ) {
	clear();
	*this = m;
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SystemError::clear() {
	this->uid = Utilities::randomInt64();
	this->uid = _GET_Abs(this->uid);
	this->timestamp = Utilities::std_time();
	this->priority = SystemError::UNKNOWN;
	this->fault_type = SystemError::FAULT_UNKNOWN;
	this->error_code = 0;
	this->method = "";
	this->description = "";
	this->context = "";
	this->tips = "";
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS

SystemError& SystemError::operator=(const SystemError& m){
	if (this == &m) return *this; // same object?
	this->uid = m.uid;
	this->timestamp = m.timestamp;
	this->priority = m.priority;
	this->fault_type = m.fault_type;
	this->source = m.source;
	this->error_code = m.error_code;
	this->method = m.method;
	this->description = m.description;
	this->context = m.context;
	this->tips = m.tips;
	return *this;
}

bool SystemError::operator==(const SystemError& other) const {
	if ( this->uid == other.uid ) return false;
	if ( this->error_code == other.error_code ) return false;
	if ( this->method == other.method ) return false;
	if ( this->priority == other.priority ) return false;
	if ( this->description == other.description ) return false;
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string SystemError::getBrief( unsigned int max ) const {
	if ( this->description.empty() ) return "";
	if ( this->description.size() < max ) return this->description;
	return this->description.substr(0, max-2) + "..";
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string SystemError::toString() const {
	return Utilities::format("%s#%"PRId64, getClassNameOnlyOf(getClassName()), this->uid);
 }

bool SystemError::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->uid ) & ret;
	ret = hash_context->update( this->timestamp ) & ret;
	ret = hash_context->update( this->priority ) & ret;
	ret = this->source.updateHash(hash_context) & ret;
	ret = hash_context->update( this->error_code ) & ret;
	ret = hash_context->update( this->method ) & ret;
	ret = hash_context->update( this->description ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int64 SystemError::raise( const Object* source, SystemError::ERROR_PRIORITY err_priority, long err_code, const string& method_name, LogContext* log ) {
	SystemError* sysError = new SystemError();
	sysError->method = method_name;
	sysError->priority = err_priority;
	sysError->error_code = err_code;
	if ( source != NULL )
		sysError->source = source->getURI();
	int64 err_id = sysError->uid;
	LogLevel type = LOG_UNKNOWN;
	switch(err_priority) {
		case SystemError::INFO:		type = LOG_INFO; break;
		case SystemError::WARNING:	type = LOG_WARNING; break;
		case SystemError::CRITICAL:	type = LOG_CRITICAL; break;
		default:					type = LOG_VERBOSE; break;
	 }
	if ( log != NULL )
		log->operator()(type, "SystemError#%ld Raised!", err_id );
	SystemMonitor::raiseError( sysError, source );
	return err_id;
 }

int64 SystemError::raise( const Object* source, LogContext& log, SystemError::ERROR_PRIORITY err_priority, long err_code, const string& method_name, const char* stringFormat, ... ) {
	SystemError* sysError = new SystemError();
	sysError->method = method_name;
	sysError->priority = err_priority;
	sysError->error_code = err_code;
	if ( source != NULL )
		sysError->source = source->getURI();
	int64 err_id = sysError->uid;
	char msgbuffer[512];
	int offset = method_name.size();
	memset(msgbuffer,'\0',512);
	if ( offset > 0 ) {
		strncpy(msgbuffer, ":", 1 );
		strncpy(msgbuffer+1, method_name.c_str(), method_name.size() );
		++offset;
		strncpy(msgbuffer+offset, "() ", 3 );
		offset += 3;
	 }
	va_list list;
	va_start(list,stringFormat);
#ifdef _OS_WINCE
	vsprintf(msgbuffer+offset,stringFormat, list);
#elif defined(WIN32_VS)
	vsprintf_s(msgbuffer+offset, 512-offset, stringFormat, list);
#else
	vsprintf(msgbuffer+offset,stringFormat, list);
#endif
	va_end(list);
	LogLevel type = LOG_UNKNOWN;
	switch(err_priority) {
		case SystemError::INFO:		type = LOG_INFO; break;
		case SystemError::WARNING:	type = LOG_WARNING; break;
		case SystemError::CRITICAL:	type = LOG_CRITICAL; break;
		default:					type = LOG_VERBOSE; break;
	 }
	log(type, msgbuffer );
	sysError->description = string(msgbuffer+offset);
	SystemMonitor::raiseError( sysError, source );
	return err_id;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<SystemError>* SystemError::getDataFields() {
	DataFieldCollector<SystemError>* fields = new DataFieldCollector<SystemError>();
	fields->add<int64, Variant>(&SystemError::getID, "ID", 0);
	fields->add<int64, Variant_DT>(&SystemError::getTimeStamp, "TimeStamp", 1);
	fields->add<const URI&, URI>(&SystemError::getSource, "Source", 2);
	fields->add<const std::string&, Variant>(&SystemError::getMethod, "Method", 3);
	fields->add<SystemError::ERROR_PRIORITY, Variant>(&SystemError::getPriority, "Priority", 4);
	fields->add<const std::string&, Variant>(&SystemError::getDescription, "Description", 5);
	fields->add<long, Variant>(&SystemError::getErrorCode, "ErrorCode", 6);
	fields->add<const std::string&, Variant>(&SystemError::getContext, "Context", 7, false);
	fields->add<const std::string&, Variant>(&SystemError::getTips, "Tips", 8, false);
	return fields;
 }

IDataFieldWrapper<SystemError>* SystemError::getPrimaryKeyWrapper() {
	DataFieldWrapperT<SystemError, int64, Variant>* dfw;
	dfw = new DataFieldWrapperT<SystemError, int64, Variant>(&SystemError::getID, "ID", 0);
	return dfw;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* SystemErrorPriorityStr[] = {
	"UNKNOWN",	// (ERROR_PRIORITY)0
	"INFO",		// (ERROR_PRIORITY)1
	"WARNING",	// (ERROR_PRIORITY)2
	"CRITICAL"	// (ERROR_PRIORITY)3
 };

Enum2StrFromArray(SystemError::getPriorityAsString, SystemError::ERROR_PRIORITY, INFO, CRITICAL, 0, SystemErrorPriorityStr)

const char* SystemError::getFaultTypeAsString( SystemError::FAULT_TYPE fault_type ) {
	switch(fault_type) {
		case FAULT_TRANSIENT:		return "TRANSIENT";
		case FAULT_INTERMITTENT:	return "INTERMITTENT";
		case FAULT_PERMANENT:		return "PERMANENT";
		case FAULT_UNKNOWN:
		default:					return "UNKNOWN";
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Message* SystemError::createMessage( Object* object, long error_code, ERROR_PRIORITY priority, const string& description ) {
	SystemError* sysError = new SystemError(error_code, description, priority);
	Message* msg_system = new Message();
	msg_system->setSource( object );
	msg_system->setContent( sysError );
	msg_system->setDestinationBroadcast();
	return msg_system;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
